﻿using Interpreter;
using Microsoft.Win32;
using Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Xceed.Wpf.Toolkit;
using Zellularautomat.Structures;

namespace Zellularautomat
{
	/// <summary>
	/// Interaktionslogik für WindowPreferences.xaml
	/// </summary>
	public partial class WindowPreferences : Window
	{
		private Controller controller;
		List<CellDefinition> cellDefinitions = new List<CellDefinition>();
		Dictionary<CellDefinition, Color> colorsForCells = new Dictionary<CellDefinition, Color>();
		Preferences preferences = new Preferences();
		string[] rules = null;
		int pxBetweenLines = 29;

		private OpenFileDialog openFileDialog = new OpenFileDialog();

		public Preferences Preferences
		{
			get { return preferences; }
			set { preferences = value; }
		}

		public List<CellDefinition> CellDefinitions
		{
			get { return cellDefinitions; }
			set { cellDefinitions = value; }
		}

		public Dictionary<CellDefinition, Color> ColorsForCells
		{
			get { return colorsForCells; }
			set { colorsForCells = value; }
		}

		public string[] Rules
		{
			get { return rules; }
			set { rules = value; }
		}
		public WindowPreferences(Controller controller)
		{
			this.controller = controller;
			InitializeComponent();
		}

		public WindowPreferences(Controller controller, List<CellDefinition> cellDefinitions, Dictionary<CellDefinition, Color> colorsForCells, Preferences preferences, string[] rules)
		{
			this.controller = controller;
			this.cellDefinitions = cellDefinitions;
			this.colorsForCells = colorsForCells;
			this.preferences = preferences;
			this.rules = rules;
			InitializeComponent();
		}

		private void Window_Initialized(object sender, EventArgs e)
		{
			if (cellDefinitions.Count > 0 && preferences != null)
			{
				ShowCellTypes();
				ShowEnvironment();
				ShowRules();
			}
		}

		private void btnOK_Click(object sender, RoutedEventArgs e)
		{
			DialogResult = true;
		}

		private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			if (DialogResult == true)
			{
				try
				{
					ValidateUserInput();
					GetUIValues();
					controller.InterpreteRules(rules);
				}
				catch (Exception ex)
				{
					e.Cancel = true;
					DialogResult = false;
					ErrorHandler.ShowError(ex.Message, "Eingabefehler");
					return;
				}
			}
		}

		#region CellTypes

		#region Events

		private void tbCtrlPreferences_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			if (cellDefinitions.Count == 0 && !tbCtrlPreferences.SelectedItem.Equals(tbPgCellTypes))
			{
				ErrorHandler.ShowInformation("Bitte laden Sie erst die verwendeten Zelltypen."
					, "Keine Daten vorhanden");
				tbCtrlPreferences.SelectedIndex = 0;
			}
		}

		private void btnOpenCellDefinitionFile_Click(object sender, RoutedEventArgs e)
		{
			OpenFile(openFileDialog, txtBxCellDefinitionFileName);
		}

		private void btnLoadCellDefinitionFile_Click(object sender, RoutedEventArgs e)
		{
			LoadCellDefinitions();
		}

		void colorChangeButton_Click(object sender, EventArgs e)
		{
			colorsForCells[cellDefinitions[(int)((ColorPicker)sender).Tag]] = ((ColorPicker)sender).SelectedColor;
		}

		#endregion
		#region Work

		private void LoadCellDefinitions()
		{
			try
			{
				if (File.Exists(txtBxCellDefinitionFileName.Text))
				{
					cellDefinitions = controller.LoadCellDefinitions(txtBxCellDefinitionFileName.Text);
					ShowCellTypes();
				}
				else
				{
					throw new ArgumentException("Die ausgewählte Datei existiert nicht");
				}
			}
			catch (Exception ex)
			{
				ErrorHandler.ShowError(ex.Message, "Fehler");
			}
		}

		Dictionary<CellDefinition, ColorPicker> colorPickers = new Dictionary<CellDefinition, ColorPicker>();

		private void ShowCellTypes()
		{
			ClearCellsUI();
			List<HSBColor> colors = CreateColors(cellDefinitions);
			Dictionary<CellDefinition, double> cellDistribution = GetCellDistribution();
			for (int i = 0; i < cellDefinitions.Count; i++)
			{
				Label l = new Label()
				{
					Content = cellDefinitions[i].Name,
					Width = 94
				};
				cnvCells.Children.Add(l);
				Canvas.SetLeft(l, 6);
				Canvas.SetTop(l, 11 + i * pxBetweenLines);
				ColorPicker cp = new ColorPicker()
				{
					SelectedColor = colors[i].ToRGB(),
					DisplayColorAndName = false,
					ShowAdvancedButton = true,
					Width = 85,
					Tag = i,
					TabIndex = i
				};
				cnvCells.Children.Add(cp);
				Canvas.SetLeft(cp, 200);
				Canvas.SetTop(cp, 6 + i * pxBetweenLines);
				colorPickers.Add(cellDefinitions[i], cp);
				cp.SelectedColorChanged += colorChangeButton_Click;
				if (!colorsForCells.ContainsKey(cellDefinitions[i]))
				{
					colorsForCells.Add(cellDefinitions[i], colors[i].ToRGB());
				}
				DoubleUpDown numericUpDown = new DoubleUpDown()
				{
					Width = 70,
					Height = 20,
					Minimum = 0,
					Maximum = 100,
					Increment = 0.01,
					FormatString = "P2",
					Tag = i,
					TabIndex = 2 * i,
					Value = cellDistribution[cellDefinitions[i]]
				};
				cnvCells.Children.Add(numericUpDown);
				Canvas.SetTop(numericUpDown, 6 + i * pxBetweenLines);
				Canvas.SetLeft(numericUpDown, 300);
				numericUpDowns.Add(cellDefinitions[i], numericUpDown);
				numericUpDown.ValueChanged += numericUpDown_ValueChanged;
			}
			lblSumOfCells = new Label()
			{
				Content = GetSumFormatString(),
				Width = 80
			};
			cnvCells.Children.Add(lblSumOfCells);
			Canvas.SetLeft(lblSumOfCells, 300);
			Canvas.SetTop(lblSumOfCells, 6 + cellDefinitions.Count * pxBetweenLines);
			cnvCells.Height = (cellDefinitions.Count + 2) * pxBetweenLines;
		}

		private void ClearCellsUI()
		{
			cnvCells.Children.Clear();
			colorPickers.Clear();
			numericUpDowns.Clear();
		}

		private void UpdateCells()
		{
			foreach (var cellDistribution in preferences.CellDistribution)
			{
				if (numericUpDowns.ContainsKey(cellDistribution.Key))
				{
					numericUpDowns[cellDistribution.Key].Value = cellDistribution.Value;
				}
			}

			foreach (var colorForCell in preferences.ColorsForCells)
			{
				if (colorPickers.ContainsKey(colorForCell.Key))
				{
					colorPickers[colorForCell.Key].SelectedColor = colorForCell.Value;
				}
			}

		}

		private List<HSBColor> CreateColors(List<CellDefinition> cellDefinitions)
		{
			int countOfDifferentColors = cellDefinitions.Count;

			List<HSBColor> colors = new List<HSBColor>();
			int distance = 360 / countOfDifferentColors;

			for (int i = 0; i < countOfDifferentColors; i++)
			{
				if (colorsForCells.ContainsKey(cellDefinitions[i]))
				{
					colors.Add(HSBColor.FromRGB(colorsForCells[cellDefinitions[i]]));
				}
				else
				{
					colors.Add(new HSBColor(i * distance, 1, 1));
				}

			}
			return colors;
		}

		#endregion
		#endregion

		#region Environment

		private void btnOpenEnvironmentFile_Click(object sender, RoutedEventArgs e)
		{
			OpenFile(openFileDialog, txtBxEnvironmentFileName);
		}

		private void btnLoadEnvironmentFile_Click(object sender, RoutedEventArgs e)
		{
			LoadEnvironment();
		}

		private void LoadEnvironment()
		{
			try
			{
				if (File.Exists(txtBxEnvironmentFileName.Text))
				{
					preferences = controller.LoadPreferences(txtBxEnvironmentFileName.Text);
					ShowEnvironment();
					UpdateCells();
				}
				else
				{
					throw new ArgumentException("Die ausgewählte Datei existiert nicht");
				}
			}
			catch (Exception ex)
			{
				ErrorHandler.ShowError(ex.Message, "Fehler");
			}
		}

		private void ShowEnvironment()
		{
			txtBxSizeHeight.Text = preferences.SizeHeight.ToString();
			txtBxSizeWidth.Text = preferences.SizeWidth.ToString();
			cmBoxEnvironmentType.SelectedItem = preferences.EnvironmentType;
			cmBxActualizationType.SelectedItem = preferences.ActualizationType;
			txtBxActualisationTime.Text = preferences.ActualizationInterval.ToString();
		}

		#endregion

		#region Initialization

		private void numericUpDown_ValueChanged(object sender, EventArgs e)
		{
			lblSumOfCells.Content = GetSumFormatString();
		}

		private Dictionary<CellDefinition, DoubleUpDown> numericUpDowns = new Dictionary<CellDefinition, DoubleUpDown>();
		private Label lblSumOfCells;
		private double sumOfCells = 0;

		private object GetSumFormatString()
		{
			sumOfCells = GetSumOfCells();
			return string.Format("{0:0%}", sumOfCells);
		}

		private Dictionary<CellDefinition, double> GetCellDistribution()
		{
			Dictionary<CellDefinition, double> distribution = new Dictionary<CellDefinition, double>();
			List<CellDefinition> remainingCellTypes = new List<CellDefinition>();
			double sum = 0;

			foreach (var cellDefinition in cellDefinitions)
			{
				if (preferences.CellDistribution.ContainsKey(cellDefinition))
				{
					sum += preferences.CellDistribution[cellDefinition];
					distribution.Add(cellDefinition, preferences.CellDistribution[cellDefinition]);
				}
				else
				{
					remainingCellTypes.Add(cellDefinition);
				}
			}
			//Fill remaining cellTypes to 100%
			foreach (var remainingType in remainingCellTypes)
			{
				double value = (1d - sum) < 0 ? 0d : (1d - sum) / remainingCellTypes.Count;
				distribution.Add(remainingType, value);
			}

			// Show Error message
			if (remainingCellTypes.Count > 0 && remainingCellTypes.Count < cellDefinitions.Count)
			{
				ErrorHandler.ShowInformation("Nicht für alle Zelltypen konnte die Anfangsverteilung geladen werden.",
					"Hinweis");
			}
			return distribution;
		}

		private double GetSumOfCells()
		{
			double count = 0;
			foreach (var numUpDown in numericUpDowns.Values)
			{
				count += numUpDown.Value == null ? 0 : (double)numUpDown.Value;
			}
			return count;
		}

		#endregion

		#region Events

		private void btnOK_Click(object sender, System.EventArgs e)
		{
			DialogResult = true;
		}

		private void btnLoadRulesFile_Click(object sender, RoutedEventArgs e)
		{
			LoadRules();
		}

		private void btnOpenRulesFile_Click(object sender, RoutedEventArgs e)
		{
			OpenFile(openFileDialog, txtBxRulesFileName);
		}

		private void SelectAddress(object sender, RoutedEventArgs e)
		{
			TextBox tb = (sender as TextBox);
			if (tb != null)
			{
				tb.SelectAll();
			}
		}

		private void SelectivelyIgnoreMouseButton(object sender, MouseButtonEventArgs e)
		{
			TextBox tb = (sender as TextBox);
			if (tb != null)
			{
				if (!tb.IsKeyboardFocusWithin)
				{
					e.Handled = true;
					tb.Focus();
				}
			}
		}
		#endregion

		#region Work

		private void LoadRules()
		{
			try
			{
				if (File.Exists(txtBxRulesFileName.Text))
				{
					txtBxRuleDefinitions.Clear();
					var rules = controller.LoadRules(txtBxRulesFileName.Text);
					foreach (var rule in rules)
					{
						txtBxRuleDefinitions.AppendText(rule + System.Environment.NewLine);
					}
				}
				else
				{
					throw new ArgumentException("Die ausgewählte Datei existiert nicht");
				}
			}
			catch (Exception ex)
			{
				ErrorHandler.ShowError(ex.Message, "Fehler");
			};
		}

		private void ShowRules()
		{
			string text = string.Empty;
			foreach (var s in rules)
			{
				text += s + System.Environment.NewLine;
			}
			txtBxRuleDefinitions.Text = text;
		}

		private void OpenFile(OpenFileDialog openFileDialog, TextBox textBox)
		{
			if (openFileDialog.ShowDialog() == true)
			{
				textBox.Text = openFileDialog.FileName;
			}
		}

		private void ValidateUserInput()
		{
			if (cellDefinitions == null || cellDefinitions.Count == 0)
			{
				throw new ArgumentException("Bitte geben Sie Zelltypen an.");
			}

			int sizeHeight;
			int sizeWidth;
			long timeTillAct;
			if (!int.TryParse(txtBxSizeHeight.Text, out sizeHeight))
			{
				throw new ArgumentException("Bitte geben Sie eine Ganzzahl für die Höhe an.");
			}
			if (!int.TryParse(txtBxSizeWidth.Text, out sizeWidth))
			{
				throw new ArgumentException("Bitte geben Sie eine Ganzzahl für die Breite an.");
			}
			if (!long.TryParse(txtBxActualisationTime.Text, out timeTillAct))
			{
				throw new ArgumentException("Bitte geben Sie eine Ganzzahl für die Aktualisierungszeit an.");
			}

			int maxWidth = (int)GetMaxSizeWidth();
			int maxHeight = (int)GetMaxSizeHeight();
			if (sizeWidth <= 0 || sizeWidth > maxWidth)
			{
				throw new ArgumentException("Breite muss zwischen 0 und " + maxWidth + " liegen.");
			}
			if (sizeHeight <= 0 || sizeHeight > maxHeight)
			{
				throw new ArgumentException("Höhe muss zwischen 0 und " + maxHeight + " liegen.");
			}
			if (txtBxRuleDefinitions.Text == string.Empty)
			{
				throw new ArgumentException("Es müssen Regeln definiert werden.");
			}
			if (sumOfCells != 1)
			{
				throw new ArgumentException("Die Summe der Anfangsverteilung muss 100% ergeben.");
			}
		}

		private double GetMaxSizeWidth()
		{
			return ((WindowMain)this.Owner).stPanelMain.ActualWidth;
		}

		private double GetMaxSizeHeight()
		{
			return ((WindowMain)this.Owner).stPanelMain.ActualHeight - ((WindowMain)this.Owner).mainMenu.ActualHeight;
		}

		private void GetUIValues()
		{
			preferences.EnvironmentType = (EnvironmentType)cmBoxEnvironmentType.SelectedItem;
			preferences.ActualizationType = (ActualizationType)cmBxActualizationType.SelectedItem;
			preferences.SizeHeight = int.Parse(txtBxSizeHeight.Text);
			preferences.SizeWidth = int.Parse(txtBxSizeWidth.Text);
			preferences.ActualizationInterval = int.Parse(txtBxActualisationTime.Text);
			Dictionary<CellDefinition, double> cellDistribution = new Dictionary<CellDefinition, double>();
			foreach (var numUpDown in numericUpDowns)
			{
				cellDistribution.Add(numUpDown.Key, (double)numUpDown.Value.Value);
			}
			preferences.CellDistribution = cellDistribution;
			rules = txtBxRuleDefinitions.Text.Split(new string[] { System.Environment.NewLine },
				StringSplitOptions.RemoveEmptyEntries);
		}

		#endregion
	}
}
